﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Reflection.Emit;

namespace Ice.ChinaKnowledge.Data
{
    public static class EntityBuilder<T>
    {
        /// <summary>
        /// 动态程序集名称
        /// </summary>
        public const String DYNAMIC_ASSEMBLY_NAME = "Ice.ChinaKnowledge.Data.Entity";
        /// <summary>
        /// 动态命名空间名称
        /// </summary>
        public const String DYNAMIC_NAMESPACE_NAME = "Ice.ChinaKnowledge.Data.Entity";

        private static AssemblyBuilder assemblyBuilder;

        private static ModuleBuilder moduleBuilder;
        /// <summary>
        /// 基类类型
        /// </summary>
        private static Type baseType = typeof(DataTransfer);
        /// <summary>
        /// 缓存
        /// </summary>
        private static Dictionary<Type, Type> _entityTypeChace = new Dictionary<Type, Type>();
        /// <summary>
        /// 缓存锁
        /// </summary>
        private static object _lockObject = new object();

        static EntityBuilder()
        {
            //获取当前应用程序域
            AppDomain currentDomain = AppDomain.CurrentDomain;
            //设置程序集名称
            AssemblyName assemblyName = new AssemblyName(DYNAMIC_ASSEMBLY_NAME);
            //设置动态程序集，并将模式设置为可执行与保存状态
            assemblyBuilder = currentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
            //设置动态名称空间
            moduleBuilder = assemblyBuilder.DefineDynamicModule(DYNAMIC_NAMESPACE_NAME);
        }

        public static Type CreateType()
        {
            Type itype = typeof(T);

            if (!itype.IsInterface) throw new Exception("The type is not interface.");

            if (!_entityTypeChace.ContainsKey(itype))
            {
                lock (_lockObject)
                {
                    if (!_entityTypeChace.ContainsKey(itype))
                    {
                        //定义一个动态类型
                        TypeBuilder typeBuilder = moduleBuilder.DefineType(
                            String.Format("{0}Entity", itype.Name),
                            TypeAttributes.Class | TypeAttributes.Public,
                            baseType, new Type[] { itype });

                        var properites = itype.GetProperties();

                        foreach (var p in properites)
                        {
                            //定义一个属性
                            PropertyBuilder propBuilder = typeBuilder.DefineProperty(
                                p.Name,
                                PropertyAttributes.None,
                                p.PropertyType,
                                null);
                            //定义属性的GET方法
                            MethodBuilder getPropertyABuilder = typeBuilder.DefineMethod(
                                "get_" + p.Name,
                                MethodAttributes.Public | MethodAttributes.Virtual,
                                p.PropertyType,
                                Type.EmptyTypes);
                            //GET指令
                            ILGenerator getAIL = getPropertyABuilder.GetILGenerator();

                            getAIL.Emit(OpCodes.Ldarg_0);
                            getAIL.Emit(OpCodes.Ldstr, p.Name);
                            getAIL.Emit(OpCodes.Call, baseType.GetMethod("get_Item"));
                            getAIL.Emit(p.PropertyType.IsValueType ? OpCodes.Unbox_Any : OpCodes.Castclass, p.PropertyType);
                            getAIL.Emit(OpCodes.Ret);
                            //定义属性的SET方法    
                            MethodBuilder setPropertyABuilder = typeBuilder.DefineMethod(
                                "set_" + p.Name,
                                MethodAttributes.Public | MethodAttributes.Virtual, null,
                                new Type[] { p.PropertyType });
                            //SET指令
                            ILGenerator setAIL = setPropertyABuilder.GetILGenerator();

                            setAIL.Emit(OpCodes.Ldarg_0);
                            setAIL.Emit(OpCodes.Ldstr, p.Name);
                            setAIL.Emit(OpCodes.Ldarg_1);

                            if (p.PropertyType.IsValueType)
                            {
                                setAIL.Emit(OpCodes.Box, p.PropertyType);
                            }

                            setAIL.Emit(OpCodes.Call, baseType.GetMethod("set_Item"));
                            setAIL.Emit(OpCodes.Ret);

                            propBuilder.SetGetMethod(getPropertyABuilder);
                            propBuilder.SetSetMethod(setPropertyABuilder);
                        }

                        _entityTypeChace[itype] = typeBuilder.CreateType();
                        //assemblyBuilder.Save(DYNAMIC_ASSEMBLY_NAME + "123.dll");
                    }
                }
            }


            return _entityTypeChace[itype];
        }

        public static T CreateInstance()
        {
            Type t = CreateType();

            return (T)Activator.CreateInstance(t);
        }
    }
}
